package com.r7data.dbstruct.meta;

import java.sql.ResultSetMetaData;

import com.r7data.dbstruct.code.DbConstantCode;
import com.r7data.dbstruct.code.ValueMetaCode;
import com.r7data.dbstruct.entity.ColumnMetaInfo;

public class ValueMetaBase {

	public ColumnMetaInfo getValueFromSQLType(ResultSetMetaData rdms, int index, int databaseType) throws Exception {
		
		boolean ignoreLength = false;//无用
		boolean lazyConversion = false;//无用
		
		ColumnMetaInfo valueInfo = null;
		
		try {
			int length = -1;
			int precision = -1;
			int valtype = ValueMetaCode.TYPE_NONE;

			boolean isClob = false;

			// 列名
			String fieldName = rdms.getColumnName(index);

			// 类型
			int type = rdms.getColumnType(index);

			// 是否有符号
			boolean signed = rdms.isSigned(index);

			switch (type) {
			case java.sql.Types.CHAR:
			case java.sql.Types.VARCHAR:
			case java.sql.Types.NVARCHAR:
			case java.sql.Types.LONGVARCHAR: // Character Large Object
				valtype = ValueMetaCode.TYPE_STRING;

				length = rdms.getColumnDisplaySize(index);

				break;

			case java.sql.Types.CLOB:
			case java.sql.Types.NCLOB:
				valtype = ValueMetaCode.TYPE_STRING;

				// length = DatabaseMeta.CLOB_LENGTH;
				length = 9999999;

				isClob = true;
				break;

			case java.sql.Types.BIGINT:
				// verify Unsigned BIGINT overflow!
				if (signed) {
					valtype = ValueMetaCode.TYPE_INTEGER;

					precision = 0; // Max 9.223.372.036.854.775.807

					length = 15;
				} else {
					valtype = ValueMetaCode.TYPE_BIGNUMBER;

					precision = 0; // Max 18.446.744.073.709.551.615
					length = 16;
				}
				break;

			case java.sql.Types.INTEGER:
				valtype = ValueMetaCode.TYPE_INTEGER;

				precision = 0; // Max 2.147.483.647
				length = 9;
				break;

			case java.sql.Types.SMALLINT:
				valtype = ValueMetaCode.TYPE_INTEGER;

				precision = 0; // Max 32.767
				length = 4;
				break;

			case java.sql.Types.TINYINT:
				valtype = ValueMetaCode.TYPE_INTEGER;

				precision = 0; // Max 127
				length = 2;
				break;

			case java.sql.Types.DECIMAL:
			case java.sql.Types.DOUBLE:
			case java.sql.Types.FLOAT:
			case java.sql.Types.REAL:
			case java.sql.Types.NUMERIC:
				valtype = ValueMetaCode.TYPE_NUMBER;

				length = rdms.getPrecision(index);
				precision = rdms.getScale(index);
				if (length >= 126) {
					length = -1;
				}
				if (precision >= 126) {
					precision = -1;
				}

				if (type == java.sql.Types.DOUBLE || type == java.sql.Types.FLOAT || type == java.sql.Types.REAL) {
					if (precision == 0) {
						precision = -1; // precision is obviously incorrect if
										// the type if
						// Double/Float/Real
					}

					// MySQL: max resolution is double precision floating point
					// (double)
					// The (12,31) that is given back is not correct
					if (DbConstantCode.database_mysql == databaseType) {
						if (precision >= length) {
							precision = -1;
							length = -1;
						}
					}

					// if the length or precision needs a BIGNUMBER
					if (length > 15 || precision > 15) {
						valtype = ValueMetaCode.TYPE_BIGNUMBER;

					}
				} else {
					if (precision == 0) {
						if (length <= 18 && length > 0) { // Among others Oracle
															// is affected
							// here.
							valtype = ValueMetaCode.TYPE_INTEGER; // Long
																	// can
																	// hold
																	// up to
																	// 18
							// significant digits
						} else if (length > 18) {
							valtype = ValueMetaCode.TYPE_BIGNUMBER;

						}
					} else { // we have a precision: keep NUMBER or change to
								// BIGNUMBER?
						if (length > 15 || precision > 15) {
							valtype = ValueMetaCode.TYPE_BIGNUMBER;

						}
					}
				}


				if (DbConstantCode.database_oracle == databaseType) {
					if (precision == 0 && length == 38) {
						valtype = ValueMetaCode.TYPE_INTEGER;

					}
					if (precision <= 0 && length <= 0) {
						// undefined size: BIGNUMBER,
						// precision on Oracle can be 38, too
						// big for a Number type
						valtype = ValueMetaCode.TYPE_BIGNUMBER;

						length = -1;
						precision = -1;
					}
				}

				break;

			case java.sql.Types.TIMESTAMP:
				// if (databaseMeta.supportsTimestampDataType()) {
				if (true) {
					valtype = ValueMetaCode.TYPE_TIMESTAMP;

					length = rdms.getScale(index);
				}
				break;

			
			case java.sql.Types.TIME:
				valtype = ValueMetaCode.TYPE_DATE;

				//
				if (DbConstantCode.database_mysql == databaseType) {
					// String property =
					// databaseMeta.getConnectionProperties().getProperty("yearIsDateType");
					String property = "yearIsDateType";
					if (property != null && property.equalsIgnoreCase("false")
							&& rdms.getColumnTypeName(index).equalsIgnoreCase("YEAR")) {
						valtype = ValueMetaCode.TYPE_INTEGER;

						precision = 0;
						length = 4;
						break;
					}
				}
				break;

			case java.sql.Types.BOOLEAN:
			case java.sql.Types.BIT:
				valtype = ValueMetaCode.TYPE_BOOLEAN;

				break;

			case java.sql.Types.BINARY:
			case java.sql.Types.BLOB:
			case java.sql.Types.VARBINARY:
			case java.sql.Types.LONGVARBINARY:
				valtype = ValueMetaCode.TYPE_BINARY;

				// db2 true; vertica true
				// if (databaseMeta.isDisplaySizeTwiceThePrecision()
				if (false && (2 * rdms.getPrecision(index)) == rdms.getColumnDisplaySize(index)) {
					// set the length for "CHAR(X) FOR BIT DATA"
					length = rdms.getPrecision(index);
				} else if (DbConstantCode.database_oracle == databaseType
						&& (type == java.sql.Types.VARBINARY || type == java.sql.Types.LONGVARBINARY)) {
					// set the length for Oracle "RAW" or "LONGRAW" data types
					valtype = ValueMetaCode.TYPE_STRING;

					length = rdms.getColumnDisplaySize(index);
					// } else if (databaseMeta.isMySQLVariant()
				} else if (DbConstantCode.database_mysql == databaseType
						&& (type == java.sql.Types.VARBINARY || type == java.sql.Types.LONGVARBINARY)) {
					// set the data type to String, see PDI-4812
					valtype = ValueMetaCode.TYPE_STRING;

					// PDI-6677 - don't call 'length =
					// rm.getColumnDisplaySize(index);'
					length = -1; // keep the length to -1, e.g. for string
									// functions (e.g.
					// CONCAT see PDI-4812)
				} else {
					length = -1;
				}
				precision = -1;
				break;

			default:
				valtype = ValueMetaCode.TYPE_STRING;

				precision = rdms.getScale(index);
				break;
			}

			String valTypeName = ValueMetaCode.getValTypeName(valtype);
			String conversionMask = getConversionMask(valtype);
			
			/** =============================== **/
			// Grab the comment as a description to the field as well.
			String comments = rdms.getColumnLabel(index);
			

			// get & store more result set meta data for later use
			int originalColumnType = rdms.getColumnType(index);
			
			String originalColumnTypeName = rdms.getColumnTypeName(index);
			
			int originalPrecision = -1;
			if (!ignoreLength) {
				// Throws exception on MySQL
				originalPrecision = rdms.getPrecision(index);
			}
			
			int originalScale = rdms.getScale(index);
	
			boolean originalSigned = false;
			try {
				originalSigned = rdms.isSigned(index);
			} catch (Exception ignored) {
				// This JDBC Driver doesn't support the isSigned method.
				// Nothing more we can do here.
			}
			
			
			/** =============================== **/

			// See if we need to enable lazy conversion...
			// mysql is false
			
			/*
			 * if (lazyConversion && valtype == ValueMetaCode.TYPE_STRING) {
			 * valueInfo.setStorageType(ValueMetaCode.STORAGE_TYPE_BINARY_STRING
			 * ); // TODO set some encoding to go with this.
			 * 
			 * // Also set the storage metadata. a copy of the parent, set to //
			 * String too. // try { ValueMetaInterface storageMetaData =
			 * ValueMetaFactory.cloneValueMeta(valueInfo,
			 * ValueMetaCode.TYPE_STRING);
			 * storageMetaData.setStorageType(ValueMetaCode.STORAGE_TYPE_NORMAL)
			 * ; valueInfo.setStorageMetadata(storageMetaData); } catch
			 * (Exception e) { throw new SQLException(e); } }
			 * 
			 * 
			 * ValueMetaInterface newV = null; try { newV =
			 * databaseMeta.getDatabaseInterface().customizeValueFromSQLType(
			 * valueInfo, rdms, index); } catch (SQLException e) { throw new
			 * SQLException(e); }
			 */
			valueInfo = new ColumnMetaInfo();
			
			valueInfo.setFieldName(fieldName);
			valueInfo.setValtype(valtype);
			valueInfo.setValTypeName(valTypeName);
			valueInfo.setLength(length);
			valueInfo.setPrecision(precision);
			//Const.NVL( v.getOrigin(), "" ) );
			//ValueMetaBase.getStorageTypeCode( v.getStorageType() ) );
			valueInfo.setConversionMask(conversionMask);
			valueInfo.setLargeTextField(isClob);
			
			valueInfo.setComments(comments);
			valueInfo.setOriginalColumnType(originalColumnType);
			valueInfo.setOriginalColumnTypeName(originalColumnTypeName);
			valueInfo.setOriginalPrecision(originalPrecision);
			valueInfo.setOriginalScale(originalScale);
			valueInfo.setOriginalSigned(originalSigned);

		} catch (Exception e) {
			throw new Exception("Error determining value metadata from SQL resultset metadata", e);
		}
		return valueInfo;
	}

	/**
	 * 
	 * @param type
	 * @return
	 */
	private String getConversionMask(int type) {
		// Set some sensible default mask on the numbers
		//
		String conversionMak = "";
		switch (type) {
		case ValueMetaCode.TYPE_INTEGER:
			conversionMak = "#;-#";
			break;
		case ValueMetaCode.TYPE_NUMBER:
			conversionMak = "#.#;-#.#";
			break;
		case ValueMetaCode.TYPE_BIGNUMBER:
			conversionMak = "#.###############################################;"
					+ "-#.###############################################";
			break;
		default:
			break;
		}
		return conversionMak;
	}

}
